﻿using System.Threading;
using System.Threading.Tasks;

namespace System.IO
{
    class TruncatedStream : Stream
    {
        private Stream _stream;
        private bool _leaveOpen;
        private long? _length;

        private long _limit;
        private long _offset;

        public TruncatedStream(Stream innerStream, long limit, bool leaveOpen)
        {
            _stream = innerStream;
            _leaveOpen = leaveOpen;
            try
            {
                _limit = Math.Min(limit, innerStream.Length - innerStream.Position);
                _length = _limit;
            }
            catch (NotSupportedException)
            {
                _limit = limit;
                _length = null;
            }
        }


        public override bool CanRead => _stream.CanRead;

        public override bool CanSeek => false;

        public override bool CanWrite => _stream.CanWrite;

        public override long Length
        {
            get
            {
                if (_length == null)
                {
                    throw new NotSupportedException();
                }
                return _length.Value;
            }
        }

        public override long Position { get => _offset; set => throw new NotSupportedException(); }



        public override void Flush()
        {
            _stream.Flush();
        }
        public override Task FlushAsync(CancellationToken cancellationToken)
        {
            return _stream.FlushAsync(cancellationToken);
        }

#if FAST_SPAN
        public override int Read(Span<byte> buffer)
        {
            if (_offset + buffer.Length > _limit)
            {
                buffer = buffer.Slice(0, (int)(_limit - _offset));
            }
            _offset += buffer.Length;
            return _stream.Read(buffer);
        }
        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
        {
            if (_offset + buffer.Length > _limit)
            {
                buffer = buffer.Slice(0, (int)(_limit - _offset));
            }
            _offset += buffer.Length;
            return _stream.ReadAsync(buffer, cancellationToken);
        }
#endif
        public override int Read(byte[] buffer, int offset, int count)
        {
            if (_offset + count > _limit)
            {
                count = (int)(_limit - _offset);
            }
            _offset += count;
            return _stream.Read(buffer, offset, count);
        }
        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            if (_offset + count > _limit)
            {
                count = (int)(_limit - _offset);
            }
            _offset += count;
            return _stream.ReadAsync(buffer, offset, count, cancellationToken);
        }


        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

#if FAST_SPAN
        public override void Write(ReadOnlySpan<byte> buffer)
        {
            if (_offset + buffer.Length > _limit)
            {
                buffer = buffer.Slice(0, (int)(_limit - _offset));
            }
            _offset += buffer.Length;
            _stream.Write(buffer);
        }
        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
        {
            if (_offset + buffer.Length > _limit)
            {
                buffer = buffer.Slice(0, (int)(_limit - _offset));
            }
            _offset += buffer.Length;
            return _stream.WriteAsync(buffer, cancellationToken);
        }
#endif
        public override void Write(byte[] buffer, int offset, int count)
        {
            if (_offset + count > _limit)
            {
                count = (int)(_limit - _offset);
            }
            _offset += count;
            _stream.Write(buffer, offset, count);
        }
        public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            if (_offset + count > _limit)
            {
                count = (int)(_limit - _offset);
            }
            _offset += count;
            return _stream.WriteAsync(buffer, offset, count, cancellationToken);
        }

        #region IDisposable Support
        private bool disposedValue = false; // 要检测冗余调用

        protected override void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // 释放托管状态(托管对象)。
                    if (!_leaveOpen)
                    {
                        _stream.Dispose();
                    }
                }

                // 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                // 将大型字段设置为 null。
                _stream = null;

                disposedValue = true;
            }

            base.Dispose(disposing);
        }

        #endregion
    }
}
